package gcdProcess;

import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;

import monitor.Monitor;
import euclid.Coordinator;
import euclid.gcdProcess;
import euclid.gcdProcessPOA;

/**
 * class for the gcd Processes
 *
 */
public class gcdProcessImpl extends gcdProcessPOA {
	private String name;
	private int id;
	private gcdProcess left;
	private gcdProcess right;
	private int Mi;
	private int timeout;
	private int delay;
	private LinkedBlockingQueue<Integer> numbersReceived;
	private Process process;
	private boolean isFinished;
	private Coordinator coordinator;
	private Monitor monitor;
	
	/**
	 * Constructor of gcdProcessImpl
	 * @param name of the process (which is the name of the starter)
	 * @param id of the process
	 * @param coordinator
	 */
	public gcdProcessImpl(String name, int id, Coordinator coordinator)
	{
		this.name = name;
		this.id = id;
		this.coordinator = coordinator;
	}

	@Override
	public void init(int startValue, int delay, int timeout, gcdProcess left,
			gcdProcess right, Monitor monitor) {
			
		this.left = left;
		this.right = right;
		this.Mi = startValue;
		this.timeout = timeout;
		this.delay = delay;
		this.monitor = monitor;
		numbersReceived = new LinkedBlockingQueue<Integer>();
		
	}

	@Override
	public int getId() {
		return id;
	}

	@Override
	public String getName() {
		return name;
	}
	
	/**
	 * Thread for the processes
	 *
	 */
	private class Process extends Thread
	{
		/**
		 * to stop the thread
		 */
		public void exit(){
			isFinished=true;
		}
		
		/**
		 * to run the thread
		 */
		public void run(){
			//boolean to stop the loop when the starter is finished
			boolean isInterrupted=false;
			while(!isInterrupted){
				try {
					isFinished = false;
					sleep(timeout*1000);
					//if the process received a (or several) number(s)
					if(numbersReceived!=null)
					{
						Integer y = numbersReceived.poll(((timeout*1000)/2), TimeUnit.MILLISECONDS);
						if(y==null)
						{
							//the process is finished
							exit();
							if(right.isFinished(name, id)){
								try{
									//display the result on the monitor
									monitor.ergebnis(getProcessId(), Mi);
								}catch(Exception e){}
								//tell the coordinator to end the calculation
								coordinator.endCalculation(Mi);
							}
							//finished the loop
							isInterrupted=true;
							try{
								//display on the monitor
								monitor.terminieren(getProcessId(),right.getProcessId(),isInterrupted);
							}catch(Exception e){}
							System.out.println(getProcessId() + " finished");
						}
						else
						{
							//to go on the calculation
							if(y<Mi)
							{
								Mi = ((Mi - 1)%y)+1;
								sleep(delay*1000);
								try{
									monitor.rechnen(left.getProcessId(),getProcessId(),Mi);
									monitor.rechnen(right.getProcessId(),getProcessId(),Mi);
								}catch(Exception e){}
								left.giveNewValue(Mi);
								right.giveNewValue(Mi);
							}
							System.out.println(getProcessId() + " Mi= " + Mi);
						}
					}
				} catch (InterruptedException e) {
					System.err.println("Problem when launching process " + getProcessId());
					isInterrupted = true;
				}
			}
		}
	}

	@Override
	public void giveNewValue(int y) {
		try{
			numbersReceived.add(y);
		}catch(NullPointerException e){
		}
		
	}

	@Override
	public void startProcess() {
		//create a thread
		process = new Process();
		//run the process
		process.start();
		
	}

	@Override
	public boolean isFinished(String starter, int id) {
		if(starter.equals(this.name) && this.id == id)
		{
			return true;
		}
		if(isFinished)
		{
			return right.isFinished(starter,id);
		}
		else
		{
			return false;
		}
	}

	@Override
	public void endProcess() {
		process.exit();
		numbersReceived = null;
		
	}

	@Override
	public String getProcessId() {
		return this.getName() + this.getId();
	}

}
